from MarketData.const import CONST
from Strategy.config import STATIC, CONFIG
import logging


# [         0,              1,         2,          3,          4,          5,   ]
# [price(contain fee),  [max,min],   price,     commidty,     base,     platform]
def update_ask_bid_list(key, item, account, ask, bid):
    min_quantity = 0
    max_quantity = 0
    # 卖家ask, 我们要买
    # 帐户base余额大于等于 本次交易 卖家提出的commdity交易量 ＊ commdity价格
    if account[key[2]].balance[key[1]] >= item.volume_ask * item.price_ask:
        min_quantity = CONST.minsize[key[0]]
        max_quantity = item.volume_ask
    # 帐户base余额小于 本次交易 卖家提出的commdity交易量 ＊ commdity价格
    else:
        min_quantity = CONST.minsize[key[0]]
        max_quantity = (account[key[2]].balance[key[1]] / item.price_ask)

    if CONFIG.limit_quantity < max_quantity:
        max_quantity = CONFIG.limit_quantity

    # 经过上面计算的最大交易量，要大于最小交易量要求
    if max_quantity >= min_quantity or True:
        ask.append([item.price_ask * (1 + CONST.fee[key[2]]), [max_quantity, min_quantity], item.price_ask, key[0], key[1], key[2]])
    # ---------------------------------------------------------------------
    # 买家bid，我们要卖
    # 帐户余额大于 本次交易 买家提出的交易量
    if account[key[2]].balance[key[0]] >= item.volume_bid:
        min_quantity = CONST.minsize[key[0]]
        max_quantity = item.volume_bid
    # 帐户余额小于 本次交易 卖家提出的交易量
    else:
        min_quantity = CONST.minsize[key[0]]
        max_quantity = account[key[2]].balance[key[0]]

    if CONFIG.limit_quantity < max_quantity:
        max_quantity = CONFIG.limit_quantity

    # 经过上面计算的交易量要大于该平台最小交易量要求
    if max_quantity >= min_quantity or True:
        bid.append([item.price_bid * (1 - CONST.fee[key[2]]), [max_quantity, min_quantity], item.price_bid, key[0], key[1], key[2]])


# [         0,              1,         2,          3,          4,          5,   ]
# [price(contain fee),  [max,min],   price,     commidty,     base,     platform]
def find_best_pair(asks, bids, ts_profit):

    logging.info("{0} : {1}".format('market_profit_1', bids[0][0] / asks[0][0] - 1))
    logging.info("{0} : {1}".format('market_profit_2', bids[1][0] / asks[0][0] - 1))
    logging.info("{0} : {1}".format('market_profit_3', bids[0][0] / asks[1][0] - 1))

    for ask in asks:
        for bid in bids:

            quanty_max = min(ask[1][0], bid[1][0])
            quanty_min = max(ask[1][1], bid[1][1])

            # 0.8的意思，如果以quanty_max数量做交易，交易中我卖出commdity的量等于quanty_max，
            # 但是，我实际的购买到的commdity的数量要小于quanty_max，因为存在交易手续费。所以实际操作中我们要用小于quanty_max的数量做交易
            # real_quanty = quanty_max * 0.9, 实际卖出的commdity的量等于real_quanty, 用quanty_max去买，实际得到的交易量等于real_quanty
            if quanty_max * 0.8 <= quanty_min:
                continue

            ev_profit = ts_profit[0]
            if (len(ask) + len(bid)) > 12:
                ev_profit = ts_profit[1]
            elif (len(ask) + len(bid)) == 20:
                ev_profit = ts_profit[2]
            profit = bid[0] / ask[0] - 1
            if profit > ev_profit:
                return ask, bid, profit

    return [], [], 0


def create_new_order(ask, bid):
    order_new = list()


# 打印order返回信息
def print_order_return_info(response):
    logging.info("---------------------order info return by platform----------------------")
    logging.info("{0}({1})".format('order response', response))
    logging.info("")


# 打印帐户信息
def print_account_info(account, real_account):
    logging.info("---------------------account info----------------------")
    coins_num = {}
    string = ""
    for coin in CONST.CurrencyList:
        coins_num[coin] = 0
    for key, item in account.items():
        for coin in CONST.CurrencyList:
            if coin in item.balance.keys():
                string += str(coin).ljust(5) + ":" + str(item.balance[coin]).ljust(20) + "; "
                coins_num[coin] += float(item.balance[coin])
        header = str(key)
        header = header.ljust(20)
        string = header + string
        logging.info(string)
        string = ""

    for key, item in coins_num.items():
        string += str(key).ljust(5) + ":" + str(item).ljust(20) + "; "

    if real_account is True:
        string = 'real_account'.ljust(20) + string
    else:
        string = 'local_account'.ljust(20) + string
    logging.info(string)
    logging.info("")


def print_quote_result(pairquote):
    logging.info("---------------------pairquote info----------------------")
    for key, item in pairquote.items():
        logging.info("{0}({1}={2};{3}={4};{5}={6};{7}={8})".format(key,
                                                                   'ask_price', item.price_ask,
                                                                   'ask_vol', item.volume_ask,
                                                                   'bid_price', item.price_bid,
                                                                   'bid_vol', item.volume_bid))
    logging.info("")


def print_best_bid_ask(ask, bid, profit, ts_profit):
    logging.info("---------------------best ask bid----------------------")
    logging.info("profit=" + str(profit))
    logging.info("TS_profit=" + str(ts_profit))

    logging.info("best ask=" + str(ask))

    logging.info("best bid=" + str(bid))
    logging.info("")


def print_all_bids_asks(asks, bids):
    logging.info("---------------------all asks bids lists----------------------")
    logging.info("ask")
    for item in asks:
        logging.info(item)

    logging.info("")

    logging.info("bid")
    for item in bids:
        logging.info(item)
    logging.info("")
    logging.info("")


def find_all_combination(pairquote, commodity_cur, mid_cur, base_cur, account):
    c_m_ask = []
    c_m_bid = []
    m_b_ask = []
    m_b_bid = []
    ask = []
    bid = []
    ask_all = []
    bid_all = []

    for key, item in pairquote.items():
        if key[0] == commodity_cur and key[1] == base_cur:
            update_ask_bid_list(key, item, account, ask, bid)
        elif key[0] == commodity_cur and key[1] == mid_cur:
            update_ask_bid_list(key, item, account, c_m_ask, c_m_bid)
        else:
            update_ask_bid_list(key, item, account, m_b_ask, m_b_bid)

    # 合并cm和mb的ask列表
    # [         0,              1,         2,          3,          4,          5,   ]
    # [price(contain fee) , [max,min],   price,     commidty,     base,     platform]
    for cm in c_m_ask:
        for mb in m_b_ask:
            quanty_max = min(cm[1][0], mb[1][0]/cm[0])
            quanty_min = max(cm[1][1], mb[1][1]/cm[0])
            if quanty_max >= quanty_min or True:
                ask.append([cm[0] * mb[0],
                            [quanty_max, quanty_min], cm[2], cm[3], cm[4], cm[5], mb[2], mb[3], mb[4], mb[5]])

    # 合并cm和mb的bid列表
    for cm in c_m_bid:
        for mb in m_b_bid:
            quanty_max = min(cm[1][0], mb[1][0]/cm[0])
            quanty_min = max(cm[1][1], mb[1][1]/cm[0])
            if quanty_max >= quanty_min or True:
                bid.append([cm[0] * mb[0],
                            [quanty_max, quanty_min], cm[2], cm[3], cm[4], cm[5], mb[2], mb[3], mb[4], mb[5]])

    ask = sorted(ask, key=lambda x: x[0])
    bid = sorted(bid, key=lambda x: x[0], reverse=True)

    return ask, bid


def make_some_fake_account_balance(account):

    account['trade']['binance'].balance['BTC'] = 1
    account['trade']['binance'].balance['ETH'] = 10
    account['trade']['binance'].balance['EOS'] = 100

    account['trade']['huobi'].balance['BTC'] = 1
    account['trade']['huobi'].balance['ETH'] = 1
    account['trade']['huobi'].balance['EOS'] = 100

    account['trade']['hitbtc'].balance['BTC'] = 1
    account['trade']['hitbtc'].balance['ETH'] = 10
    account['trade']['hitbtc'].balance['EOS'] = 100

    account['trade']['poloniex'].balance['BTC'] = 1
    account['trade']['poloniex'].balance['ETH'] = 10
    account['trade']['poloniex'].balance['EOS'] = 100

    account['trade']['bitfinex'].balance['BTC'] = 1
    account['trade']['bitfinex'].balance['ETH'] = 10
    account['trade']['bitfinex'].balance['EOS'] = 100

    account['trade']['bigone'].balance['BTC'] = 1
    account['trade']['bigone'].balance['ETH'] = 10
    account['trade']['bigone'].balance['EOS'] = 100


def create_orders(commodity_cur, mid_cur, base_cur, best_ask, best_bid):

    new_order = list()

    c_minsize = CONST.minsize[commodity_cur]
    c_lotsize = CONST.lotsize[commodity_cur]
    m_minsize = CONST.minsize[mid_cur]
    m_lotsize = CONST.lotsize[mid_cur]

    quanty_max = min(best_ask[1][0], best_bid[1][0])
    quanty_min = max(best_ask[1][1], best_bid[1][1])

    c_sell_quanty = int((quanty_max * 0.8) / c_lotsize) * c_lotsize
    c_buy_quanty = int((c_sell_quanty * (1 + CONST.fee[best_ask[5]])) / c_lotsize) * c_lotsize
    if c_buy_quanty < c_minsize:
        return
    m_sell_quanty = 0
    m_buy_quanty = 0

    # sell是串对，buy是单一对
    if len(best_bid) > 6 and len(best_ask) == 6:
        m_sell_quanty = int((c_sell_quanty * best_bid[2]) / m_lotsize) * m_lotsize
        if m_sell_quanty < m_minsize:
            return
    # sell是单一对，buy是串对
    elif len(best_bid) == 6 and len(best_ask) > 6:
        m_buy_quanty = int(((c_buy_quanty * best_ask[2]) * (1 + CONST.fee[best_ask[5]])) / m_lotsize) * m_lotsize
        if m_buy_quanty < m_minsize:
            return
    # sell，buy都是串对
    elif len(best_bid) > 6 and len(best_ask) > 6:
        m_sell_quanty = int((c_sell_quanty * best_bid[2]) / m_lotsize) * m_lotsize
        m_buy_quanty = int(((c_buy_quanty * best_ask[2]) * (1 + CONST.fee[best_ask[5]])) / m_lotsize) * m_lotsize
        if m_buy_quanty < m_minsize:
            return

    # [         0,              1,         2,          3,          4,          5,   ]
    # [price(contain fee),  [max,min],   price,     commidty,     base,     platform]
    if len(best_ask) > 6:
        new_order.append({'platform': best_ask[5], 'commodity_cur': best_ask[3], 'base_cur': best_ask[4],
                          'type': "buy", 'order': "market",
                          'quantity': c_buy_quanty, 'price': round(best_ask[2], 7)})
        new_order.append({'platform': best_ask[9], 'commodity_cur': best_ask[7], 'base_cur': best_ask[8],
                          'type': "buy", 'order': "market",
                          'quantity': m_buy_quanty, 'price': round(best_ask[6], 7)})
    else:
        new_order.append({'platform': best_ask[5], 'commodity_cur': best_ask[3], 'base_cur': best_ask[4],
                          'type': "buy", 'order': "market",
                          'quantity': c_buy_quanty, 'price': round(best_ask[2], 7)})

    if len(best_bid) > 6:
        new_order.append({'platform': best_bid[5], 'commodity_cur': best_bid[3], 'base_cur': best_bid[4],
                          'type': "sell", 'order': "market",
                          'quantity': c_sell_quanty, 'price': round(best_bid[2], 7)})
        new_order.append({'platform': best_bid[9], 'commodity_cur': best_bid[7], 'base_cur': best_bid[8],
                          'type': "sell", 'order': "market",
                          'quantity': m_sell_quanty, 'price': round(best_bid[6], 7)})
    else:
        new_order.append({'platform': best_bid[5], 'commodity_cur': best_bid[3], 'base_cur': best_bid[4],
                          'type': "sell", 'order': "market",
                          'quantity': c_sell_quanty, 'price': round(best_bid[2], 7)})

    return new_order


def print_new_orders(orders):
    logging.info("---------------------new orders info----------------------")
    for item in orders:
        logging.info(item)
    logging.info("")


# [         0,              1,         2,          3,          4,          5,   ]
# [price(contain fee),  [max,min],   price,     commidty,     base,     platform]
def find_best_sell_orders_we_can_make(asks, account):
    base_ask = None
    best_sell_orders_we_can_make = list()
    best_sell_orders_plat = list()
    # find best base ask
    for ask in asks:
        if ask[1][0] >= ask[1][1]:
            base_ask = ask
            break

    if base_ask is None:
        return best_sell_orders_we_can_make

    logging.info("base_ask is " + str(base_ask))

    decs_asks = sorted(asks, key=lambda x: x[0], reverse=True)
    for ask in decs_asks:
        if (len(best_sell_orders_plat) + 1) * base_ask[1][1] < base_ask[1][0]:
            if ask[2] * (1 - CONFIG.TS_limitorder) * (1 - CONST.fee[ask[5]]) > base_ask[0] * (1 + CONFIG.TS_profit[0]) and \
                    account[ask[5]].balance[ask[3]] >= base_ask[1][1]*1.1:
                best_sell_orders_plat.append(ask)

    for ask in best_sell_orders_plat:
        best_sell_orders_we_can_make.append({'platform': ask[5], 'commodity_cur': ask[3], 'base_cur': ask[4],
                                             'type': "sell", 'order': "limit", 'quantity': ask[1][1]*1.1,
                                             'price': round(ask[2] * (1 - CONFIG.TS_limitorder), 7)})

    return best_sell_orders_we_can_make


# [         0,              1,         2,          3,          4,          5,   ]
# [price(contain fee),  [max,min],   price,     commidty,     base,     platform]
def find_best_buy_orders_we_can_make(bids, account):
    base_bid = None
    best_bids_orders_we_can_make = list()
    best_bids_orders_plat = list()
    # find best base bid
    for bid in bids:
        if bid[1][0] >= bid[1][1]:
            base_bid = bid
            break

    if base_bid is None:
        return best_bids_orders_we_can_make

    logging.info("base_bid is " + str(base_bid))

    acs_bids = sorted(bids, key=lambda x: x[0], reverse=True)
    for bid in acs_bids:
        if (len(best_bids_orders_plat) + 1) * base_bid[1][1] < base_bid[1][0]:
            if bid[2] * (1 + CONFIG.TS_limitorder) * (1 + CONST.fee[bid[5]]) < base_bid[0] * (1 - CONFIG.TS_profit[0]) and \
                    account[bid[5]].balance[bid[4]] / bid[2] >= base_bid[1][1]*1.1:
                best_bids_orders_plat.append(bid)

    for bid in best_bids_orders_plat:
        best_bids_orders_we_can_make.append({'platform': bid[5], 'commodity_cur': bid[3], 'base_cur': bid[4],
                                             'type': "buy", 'order': "limit", 'quantity': bid[1][1]*1.1,
                                             'price': round(bid[2] * (1 + CONFIG.TS_limitorder), 7)})

    return best_bids_orders_we_can_make


def print_new_ordor(best_sell_orders_we_can_make, best_buy_orders_we_can_make):
    logging.info("------------------best_sell_orders_we_can_make-------------------------")
    for item in best_sell_orders_we_can_make:
        logging.info(item)

    logging.info("------------------best_buy_orders_we_can_make-------------------------")
    for item in best_buy_orders_we_can_make:
        logging.info(item)